#include "test_byte.h"
#include "string.h"
#include "stdlib.h"

#include "esp_timer.h"
#include "esp_log.h"
#include "esp_sleep.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"

#define MODEL_NAME "pin"


static QueueHandle_t gpio_evt_queue = NULL;

//测试模板
typedef struct {
    int pin;
    int funcIndex;



    uint32_t lastTime;//上一次的时间戳
    int delayTime;//延时消抖时间
    lua_State*L;//用于lua回调
}user_pin;

//中断函数中不能做太多操作，所以丢在队列中
static void IRAM_ATTR gpio_isr_handler(void* arg){
    user_pin**temp=arg;
    uint32_t currtime_us = esp_timer_get_time();
    uint32_t lastTime=(*temp)->lastTime;
    uint32_t delayTime=(*temp)->delayTime;
    if(currtime_us-lastTime<=delayTime){
        return;
    }
    (*temp)->lastTime=currtime_us;
    xQueueSendFromISR(gpio_evt_queue, arg, NULL);
    //vTaskDelay(5000/portTICK_PERIOD_MS);
}


static void gpio_task_example(void* arg){
    unsigned int address;
    for (;;) {
        if (xQueueReceive(gpio_evt_queue,&address, portMAX_DELAY)) {
            user_pin*temp=(user_pin*)address;
            lua_State*L=temp->L;
            int pin=temp->pin;
            int funcIndex=temp->funcIndex;
            int level=gpio_get_level(pin);
            lua_rawgeti(L, LUA_REGISTRYINDEX, funcIndex);
            lua_pushinteger(L, pin);
            lua_pushinteger(L,level);
            int r=lua_pcall(L, 2, 0,0);
            if(r>0){
                printf("err:%s\r\n",luaL_checkstring(L,-1));
            }
        }
    }
    
}


// add 方法 
static int lua_user_isr_handler_add(lua_State *L){
    user_pin**temp=(user_pin**)lua_touserdata(L,1);
    int time=200;
    if(lua_gettop(L)==3){
        time=luaL_checkinteger(L,2);
    }
    int funcIndex = luaL_ref(L, LUA_REGISTRYINDEX);
    (*temp)->funcIndex=funcIndex;
    gpio_isr_handler_add((*temp)->pin, gpio_isr_handler, temp);
    (*temp)->lastTime=esp_timer_get_time();
    printf("lasttime1:%ld\r\n",(*temp)->lastTime);
    (*temp)->delayTime=time;
    return 0;
}


// reset 方法 
static int lua_user_isr_handler_remove(lua_State *L){
    user_pin**temp=(user_pin**)lua_touserdata(L,1);
    gpio_isr_handler_remove((*temp)->pin);
    return 0;
}
// reset 方法

// typedef enum {
//     GPIO_INTR_DISABLE = 0,     /*!< Disable GPIO interrupt                             */
//     GPIO_INTR_POSEDGE = 1,     /*!< GPIO interrupt type : rising edge                  */
//     GPIO_INTR_NEGEDGE = 2,     /*!< GPIO interrupt type : falling edge                 */
//     GPIO_INTR_ANYEDGE = 3,     /*!< GPIO interrupt type : both rising and falling edge */
//     GPIO_INTR_LOW_LEVEL = 4,   /*!< GPIO interrupt type : input low level trigger      */
//     GPIO_INTR_HIGH_LEVEL = 5,  /*!< GPIO interrupt type : input high level trigger     */
//     GPIO_INTR_MAX,
// } gpio_int_type_t;
static int lua_user_intr_type(lua_State *L){
    user_pin**temp=(user_pin**)lua_touserdata(L,1);
    int type=luaL_checkinteger(L,2);
    gpio_set_intr_type((*temp)->pin,type);
    return 0;
}

// reset 方法 
static int lua_user_reset(lua_State *L){
    user_pin**temp=(user_pin**)lua_touserdata(L,1);
    gpio_reset_pin((*temp)->pin);
    return 0;
}

// write 方法 
static int lua_user_write(lua_State *L){
    user_pin**temp=(user_pin**)lua_touserdata(L,1);
    int state=luaL_checkinteger(L,2);
    gpio_set_level((*temp)->pin, state);
    return 0;
}

// read 方法 
static int lua_user_read(lua_State *L){
    user_pin**temp=(user_pin**)lua_touserdata(L,1);
    int level=gpio_get_level((*temp)->pin);
    lua_pushinteger(L,level);
    return 1;
}

// input 方法 
static int lua_user_input(lua_State *L){
    user_pin**temp=(user_pin**)lua_touserdata(L,1);
    gpio_set_direction((*temp)->pin, GPIO_MODE_INPUT);
    return 0;
}

// output 方法 
static int lua_user_output(lua_State *L){
    user_pin**temp=(user_pin**)lua_touserdata(L,1);
    gpio_set_direction((*temp)->pin, GPIO_MODE_OUTPUT);
    return 0;
}
// tostring 方法 
static int lua_user_tostring(lua_State *L){
    lua_pushstring(L,"lua socket model");
    return 1;
}

// 执行 collectgarbage()函数会进行gc
static int lua_user_gc(lua_State *L){
    user_pin**temp=(user_pin**)lua_touserdata(L,1);
    if(*temp!=NULL){
        free(*temp);
    }

    return 0;
}

static const luaL_Reg lua_user_func[] ={ 
    {"input", lua_user_input},
    {"output", lua_user_output},
    {"read", lua_user_read},
    {"write", lua_user_write},
    {"reset", lua_user_reset},
    {"intrType", lua_user_intr_type},
    {"handlerAdd", lua_user_isr_handler_add},
    {"handlerRemove", lua_user_isr_handler_remove},


    {"__tostring", lua_user_tostring}, 
    {"__gc", lua_user_gc}, 
	{NULL, NULL}
};


// Timer.create('time1',22,call)
static int lua_create_new(lua_State *L){
     //创建一个对象指针放到stack里，返回给Lua中使用，userdata的位置-1
    int p=luaL_checkinteger(L,1);
    
    user_pin**temp = (user_pin**)lua_newuserdata(L, sizeof(user_pin*));
    user_pin*pin=(user_pin*)malloc(sizeof(user_pin));
    pin->pin=p;
    pin->L=L;
    *temp=pin;
    //Lua->stack，得到全局元表位置-1,userdata位置-2
    luaL_getmetatable(L, MODEL_NAME);
    //将元表赋值给位置-2的userdata，并弹出-1的元表
    lua_setmetatable(L, -2);
    return 1;
}

static int lua_isr_service(lua_State *L){
    gpio_install_isr_service(0);
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);
    return 0;
}

static const luaL_Reg lua_user_constructor_funcs[] = {
    { "create", lua_create_new },
    { "isrService", lua_isr_service },
    { NULL, NULL }
};

int luaopen_user_pin_libs (lua_State *L) {
     //创建全局元表（里面包含了对LUA_REGISTRYINDEX的操作），元表的位置为-1
    luaL_newmetatable(L, MODEL_NAME);

    //将元表作为一个副本压栈到位置-1，原元表位置-2
    lua_pushvalue(L, -1);

    //设置-2位置元表的__index索引的值为位置-1的元表，并弹出位置-1的元表，原元表的位置为-1
    lua_setfield(L, -2, "__index");

    //将成员函数注册到元表中（到这里，全局元表的设置就全部完成了）
    luaL_setfuncs(L, lua_user_func, 0);
    //注册构造函数到新表中，并返回给Lua
    luaL_newlib(L, lua_user_constructor_funcs);
    return 1;
}